package com.twj.tools.poi;

/**
 * @author 作者: tianwenju
 * @version 1.0
 * @project 金税三期工程核心征管及应用总集成项目
 * @package com.twj.tools.poi
 * @file DocToPdfConverter.java 创建时间:2025年08月11日 9:44
 * @title 标题（要求能简洁地表达出类的功能和职责）
 * @description 描述（简要描述类的职责、实现方式、使用注意事项等）
 * @copyright Copyright (c) 2020 中国软件与技术服务股份有限公司
 * @company 中国软件与技术服务股份有限公司
 * @module 模块: 模块名称
 * @reviewer 审核人:
 * @history 修订历史（历次修订内容、修订人、修订时间等）
 */

import com.sun.star.beans.PropertyValue;
import com.sun.star.beans.XPropertySet;
import com.sun.star.comp.helper.Bootstrap;
import com.sun.star.connection.NoConnectException;
import com.sun.star.frame.*;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.lib.uno.helper.UnoUrl;
import com.sun.star.text.XTextDocument;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;
import com.sun.star.util.URL;

import java.io.File;

public class DocToPdfConverter {

    public static void main(String[] args) {
        String inputPath = "C:\\Users\\t1318\\Desktop\\税务\\供要管用\\222.doc";  // 输入文件路径
        String outputPath = "C:\\Users\\t1318\\Desktop\\税务\\供要管用\\222.pdf"; // 输出PDF路径

        inputPath = "C:\\Users\\t1318\\Desktop\\税务\\供要管用\\gygy\\bgmb.docx";  // 输入文件路径
        outputPath = "C:\\Users\\t1318\\Desktop\\税务\\供要管用\\gygy\\bgmb.pdf"; // 输出PDF路径

        try {
//            convertDocToCleanPdf(inputPath, outputPath);
            convertDocToCleanPdfSimple(inputPath, outputPath);
            System.out.println("转换成功！");
        } catch (Exception e) {
            System.err.println("转换失败: " + e.getMessage());
            e.printStackTrace();
        }
    }


    /**
     * 将文件路径转换为正确的UNO文件URL格式
     *
     * @param path 本地文件路径
     * @return UNO文件URL
     */
    /**
     * 将文件路径转换为正确的UNO文件URL格式（改进版）
     * @param path 本地文件路径
     * @return UNO文件URL
     */
    private static String toFileUrl(String path) {
        try {
            File file = new File(path);
            String url = file.toURI().toString();
            System.out.println("原始路径: " + path);
            System.out.println("转换后URL: " + url);
            return url;
        } catch (Exception e) {
            System.err.println("URL转换异常: " + e.getMessage());
            // 手动构造URL
            String normalizedPath = path.replace("\\", "/");
            // 确保路径以file:///开头
            if (normalizedPath.contains(":")) {
                // Windows路径 C:/path -> /C:/path
                normalizedPath = "/" + normalizedPath;
            }
            return "file://" + normalizedPath;
        }
    }


    public static void convertDocToCleanPdfSimple(String inputDoc, String outputPdf) throws Exception {
        // 直接使用加载属性接受修订，跳过复杂的Dispatch操作
        XComponentContext context = getOfficeContext();
        XMultiComponentFactory serviceManager = context.getServiceManager();

        Object desktop = serviceManager.createInstanceWithContext(
                "com.sun.star.frame.Desktop", context);
        XDesktop xDesktop = UnoRuntime.queryInterface(XDesktop.class, desktop);

        // 简化加载属性 - 重点是UpdateDocMode
        PropertyValue[] loadProps = new PropertyValue[2];
        loadProps[0] = new PropertyValue();
        loadProps[0].Name = "Hidden";
        loadProps[0].Value = true;

        loadProps[1] = new PropertyValue();
        loadProps[1].Name = "UpdateDocMode";
        loadProps[1].Value = 1; // 自动接受所有修订

        XComponentLoader loader = UnoRuntime.queryInterface(XComponentLoader.class, xDesktop);
        String fileUrl = toFileUrl(inputDoc);

        XComponent document = loader.loadComponentFromURL(fileUrl, "_blank", 0, loadProps);

        if (document == null) {
            throw new Exception("无法加载文档: " + inputDoc);
        }

        // 简化的PDF导出属性
        PropertyValue[] exportProps = new PropertyValue[2];
        exportProps[0] = new PropertyValue();
        exportProps[0].Name = "FilterName";
        exportProps[0].Value = "writer_pdf_Export";

        exportProps[1] = new PropertyValue();
        exportProps[1].Name = "FilterData";
        PropertyValue[] filterData = new PropertyValue[2];
        filterData[0] = new PropertyValue();
        filterData[0].Name = "ExportTrackedChanges";
        filterData[0].Value = false;
        filterData[1] = new PropertyValue();
        filterData[1].Name = "ShowTrackedChanges";
        filterData[1].Value = false;
        exportProps[1].Value = filterData;

        String pdfUrl = toFileUrl(outputPdf);
        XStorable storable = UnoRuntime.queryInterface(XStorable.class, document);
        if (storable != null) {
            storable.storeToURL(pdfUrl, exportProps);
        } else {
            throw new Exception("文档不支持存储操作");
        }

        document.dispose();
    }

    // 新增方法：执行 LibreOffice 的 Dispatch 命令
    private static void executeDispatch(XComponent doc, String command) throws Exception {
        XModel model = UnoRuntime.queryInterface(XModel.class, doc);
        if (model == null) throw new Exception("无法获取文档模型");

        XController controller = model.getCurrentController();
        if (controller == null) throw new Exception("无法获取控制器");

        XDispatchProvider provider = UnoRuntime.queryInterface(XDispatchProvider.class, controller);
        if (provider == null) throw new Exception("控制器不支持命令分发");

        URL url = new URL();
        url.Complete = command;

        XFrame frame = controller.getFrame();

        XDispatch dispatch = provider.queryDispatch(url, "", 0);
        if (dispatch != null) {
            dispatch.dispatch(url, new PropertyValue[0]);
        } else {
            System.err.println("无法分发命令: " + command);
        }
    }


    /**
     * 根据文件扩展名获取相应的过滤器名称
     * @param fileName 文件名
     * @return 过滤器名称
     */
    private static String getFilterNameByExtension(String fileName) {
        String lowerName = fileName.toLowerCase();
        if (lowerName.endsWith(".doc")) {
            return "MS Word 97-2003";
        } else if (lowerName.endsWith(".docx")) {
            return "MS Word 2007 XML";
        } else if (lowerName.endsWith(".rtf")) {
            return "Rich Text Format";
        } else if (lowerName.endsWith(".odt")) {
            return "writer8";
        } else {
            return ""; // 空字符串表示自动检测
        }
    }

    // 替换原有 getOfficeContext 中的方案2代码块为以下内容
    private static XComponentContext connectToOffice(String connectionString) throws Exception {
        try {
            // 创建UnoUrl对象
            UnoUrl unoUrl = UnoUrl.parseUnoUrl(connectionString);

            // 获取本地上下文并初始化组件上下文
            XComponentContext xLocalContext = Bootstrap.createInitialComponentContext(null);
            XMultiComponentFactory xLocalServiceManager = xLocalContext.getServiceManager();

            // 获取UnoUrlResolver服务
            Object urlResolver = xLocalServiceManager.createInstanceWithContext(
                    "com.sun.star.bridge.UnoUrlResolver", xLocalContext);
            com.sun.star.bridge.XUnoUrlResolver xUnoUrlResolver =
                    UnoRuntime.queryInterface(com.sun.star.bridge.XUnoUrlResolver.class, urlResolver);

            // 解析远程上下文
            Object context = xUnoUrlResolver.resolve(connectionString);
            XComponentContext remoteContext = UnoRuntime.queryInterface(XComponentContext.class, context);

            return remoteContext;
        } catch (NoConnectException e) {
            System.out.println("无法连接到: " + connectionString);
            throw e;
        }
    }

    public static void convertDocToCleanPdf(String inputDoc, String outputPdf) throws Exception {
        // 建立与LibreOffice的连接
        XComponentContext context = getOfficeContext();
        XMultiComponentFactory serviceManager = context.getServiceManager();

        // 获取桌面服务
        Object desktop = serviceManager.createInstanceWithContext(
                "com.sun.star.frame.Desktop", context);
        XDesktop xDesktop = UnoRuntime.queryInterface(XDesktop.class, desktop);

        // 准备打开文档的属性（隐藏界面）
        PropertyValue[] loadProps = new PropertyValue[3];
        loadProps[0] = new PropertyValue();
        loadProps[0].Name = "Hidden";
        loadProps[0].Value = true;

        // 强制接受所有修订
        loadProps[1] = new PropertyValue();
        loadProps[1].Name = "UpdateDocMode";
        loadProps[1].Value = 1; // 1 表示 ACCEPT_ALL

        // 设置文档为只读模式避免锁定
        loadProps[2] = new PropertyValue();
        loadProps[2].Name = "ReadOnly";
        loadProps[2].Value = true;

        // 加载文档
        XComponentLoader loader = UnoRuntime.queryInterface(
                XComponentLoader.class, xDesktop);

        // 修复文件URL格式
        String fileUrl = toFileUrl(inputDoc);
        System.out.println("尝试加载文件: " + fileUrl);

        // 检查文件是否存在
        File inputFile = new File(inputDoc);
        if (!inputFile.exists()) {
            throw new Exception("输入文件不存在: " + inputDoc);
        }

        XComponent document = loader.loadComponentFromURL(
                fileUrl, "_blank", 0, loadProps);

        if (document == null) {
            throw new Exception("无法加载文档: " + inputDoc);
        }

        // 转换为文本文档接口
        XTextDocument textDoc = UnoRuntime.queryInterface(
                XTextDocument.class, document);
        if (textDoc == null) {
            throw new Exception("文件不是文本格式: " + inputDoc);
        }

        // 接受所有修订
        acceptAllRevisions(textDoc);

        // 等待一段时间确保操作完成
        Thread.sleep(1000);

        // 准备PDF导出属性 - 关键：设置不显示修订
        PropertyValue[] exportProps = new PropertyValue[3];
        exportProps[0] = new PropertyValue();
        exportProps[0].Name = "FilterName";
        exportProps[0].Value = "writer_pdf_Export";

        // 添加输出路径属性
        exportProps[1] = new PropertyValue();
        exportProps[1].Name = "Overwrite";
        exportProps[1].Value = true;

        // 关键：设置PDF导出选项，确保不显示修订痕迹
        exportProps[2] = new PropertyValue();
        exportProps[2].Name = "FilterData";
        PropertyValue[] filterData = new PropertyValue[4];
        filterData[0] = new PropertyValue();
        filterData[0].Name = "ExportTrackedChanges";
        filterData[0].Value = false;

        filterData[1] = new PropertyValue();
        filterData[1].Name = "ShowTrackedChanges";
        filterData[1].Value = false;

        filterData[2] = new PropertyValue();
        filterData[2].Name = "PrintChanges";
        filterData[2].Value = false;

        filterData[3] = new PropertyValue();
        filterData[3].Name = "PrintAnnotations";
        filterData[3].Value = false;

        exportProps[2].Value = filterData;

        // 导出为PDF（使用XStorable接口）
        String pdfUrl = toFileUrl(outputPdf);
        System.out.println("导出PDF到: " + pdfUrl);

        XStorable storable = UnoRuntime.queryInterface(XStorable.class, document);
        if (storable != null) {
            storable.storeToURL(pdfUrl, exportProps);
        } else {
            throw new Exception("文档不支持存储操作");
        }

        // 关闭文档
        document.dispose();
        // 注意：不要terminate桌面，这可能导致连接问题
        // xDesktop.terminate();
    }

    // 改进的acceptAllRevisions方法
    private static void acceptAllRevisions(XTextDocument doc) throws Exception {
        try {
            // 方法1: 通过属性设置接受修订
            XPropertySet propSet = UnoRuntime.queryInterface(XPropertySet.class, doc);
            if (propSet != null) {
                try {
                    // 尝试设置视图属性隐藏修订
                    propSet.setPropertyValue("ShowChanges", false);
                    propSet.setPropertyValue("ShowInlineChanges", false);
                    System.out.println("已通过属性设置隐藏修订显示");
                } catch (Exception e) {
                    System.out.println("设置视图属性失败: " + e.getMessage());
                }
            }

            // 方法2: 通过Dispatch命令接受修订
            System.out.println("尝试通过 Dispatch 接受所有修订");
            executeDispatch((XComponent) doc, ".uno:AcceptAllTrackedChanges");

            // 方法3: 再次尝试隐藏修订显示
            executeDispatch((XComponent) doc, ".uno:ShowTrackedChanges");

        } catch (Exception e) {
            System.out.println("接受修订过程中出现异常: " + e.getMessage());
            // 即使出现异常也继续执行，因为可能已经通过加载属性接受了修订
        }
    }

    // 改进的getOfficeContext方法
    private static XComponentContext getOfficeContext() throws Exception {
        // 方案1: 自动检测并启动LibreOffice
        try {
            // 先尝试自动检测路径
            String osName = System.getProperty("os.name").toLowerCase();
            String unoPath = "";

            if (osName.contains("windows")) {
                String[] possiblePaths = {
                        "C:\\Program Files\\LibreOffice\\program",
                        "C:\\Program Files (x86)\\LibreOffice\\program",
                        "C:\\Program Files\\LibreOffice 24\\program",  // 新版本路径
                        "C:\\Program Files (x86)\\LibreOffice 24\\program"
                };

                for (String path : possiblePaths) {
                    File pathFile = new File(path);
                    if (pathFile.exists()) {
                        unoPath = path;
                        break;
                    }
                }
            } else {
                String[] possiblePaths = {
                        "/usr/lib/libreoffice/program",
                        "/usr/lib64/libreoffice/program",
                        "/opt/libreoffice/program",
                        "/Applications/LibreOffice.app/Contents/program"
                };

                for (String path : possiblePaths) {
                    File pathFile = new File(path);
                    if (pathFile.exists()) {
                        unoPath = path;
                        break;
                    }
                }
            }

            if (!unoPath.isEmpty()) {
                System.setProperty("com.sun.star.lib.loader.unopath", unoPath);
                System.out.println("使用LibreOffice路径: " + unoPath);

                // 在启动前先尝试连接，避免重复启动
                try {
                    return connectToOffice("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext");
                } catch (Exception e) {
                    System.out.println("连接现有实例失败，尝试启动新实例");
                }

                return com.sun.star.comp.helper.Bootstrap.bootstrap();
            }
        } catch (Exception e) {
            System.out.println("自动启动LibreOffice失败: " + e.getMessage());
        }

        // 方案2: 连接到已运行的LibreOffice实例
        System.out.println("尝试连接到已运行的LibreOffice实例...");
        String[] connectionParams = {
                "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext",
                "uno:pipe,name=libreoffice;urp;StarOffice.ComponentContext"
        };

        for (String param : connectionParams) {
            try {
                return connectToOffice(param);
            } catch (NoConnectException e) {
                System.out.println("连接失败: " + param);
            }
        }

        throw new Exception("无法连接到LibreOffice服务，请确保LibreOffice已安装并运行，或手动启动：\n" +
                "Windows: soffice --headless --accept=\"socket,host=127.0.0.1,port=2002;urp;\"\n" +
                "Linux/Mac: libreoffice --headless --accept=\"socket,host=127.0.0.1,port=2002;urp;\"");
    }


}